#region Debug Preprocessor
#if !DEBUG || !TRACE
    // If not debug then take out all debugging.
    #define CPU_DEBUG_OFF
    #undef CPU_DEBUG_ALL
#endif

// Check if both are defined and raise error.
#if CPU_DEBUG_ALL && CPU_DEBUG_OFF
    #error CPU_DEBUG_ALL and CPU_DEBUG_OFF cannot both be defined
#endif

// Define cpu debug if any of the subsections debuging are turned on.
#if (CPU_MEM_DEBUG || CPU_INT_DEBUG || CPU_STACK_DEBUG || CPU_INS_DEBUG || CPU_TIMING_DEBUG) && !CPU_DEBUG_ALL && !CPU_DEBUG_OFF
    #define CPU_DEBUG
#endif

// Turn on all cpu debug.
#if CPU_DEBUG_ALL
    #define CPU_DEBUG
    #define CPU_MEM_DEBUG
    #define CPU_INT_DEBUG
    #define CPU_INS_DEBUG
    #define CPU_STACK_DEBUG
    #define CPU_TIMING_DEBUG
#endif

// Turn of all cpu debug.
#if CPU_DEBUG_OFF
    #undef CPU_DEBUG
    #undef CPU_MEM_DEBUG
    #undef CPU_INT_DEBUG
    #undef CPU_INS_DEBUG
    #undef CPU_STACK_DEBUG
    #undef CPU_TIMING_DEBUG
#endif


#endregion Debug Preprocessor

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Zsa.Emulation.Util;

namespace Zsa.Emulation.Processor
{
	public partial class Cpu6502
    {
        

        #region Fields

        #if CPU_DEBUG
        /// <summary>
        /// The current instruction.
        /// </summary>
        private long _InstructionCount;

        /// <summary>
        /// The instruciton start.
        /// </summary>
        private ushort _InstructionStart;

        /// <summary>
        /// The current op code.
        /// </summary>
        private OpCodes _CurrentOp;

        /// <summary>
        /// The op codes.
        /// </summary>
        private OpCode[] _OpCodes = new OpCode[255];

        /// <summary>
        /// The cpu trace logger.
        /// </summary>
        private DebugTraceSource _Source = TraceLogger.GetSubsystem(TraceSubsystem.Cpu);
        #endif

        #endregion Fields

        #region Debug Methods
        /// <summary>
        /// Logs the instrunction.
        /// </summary>
        [Conditional("CPU_DEBUG")]
        private void LogInstruction(OpCodes code)
        {
            #if CPU_DEBUG
            _InstructionCount++;
            _InstructionStart = _RegisterProgramCounter;
            _CurrentOp = code;
            #endif
        }

        /// <summary>
        /// Logs the instruction.
        /// </summary>
        /// <param name="instructionSize">Size of the instruction.</param>
        /// <param name="cycles">The cycles.</param>
        [Conditional("CPU_DEBUG")]
        private void LogInstruction(int instructionSize, int cycles)
        {
            #if CPU_DEBUG
            if (_OpCodes[(int)_CurrentOp] == OpCode.Empty)
            {
                OpCode _Code = new OpCode(_CurrentOp, (byte)instructionSize, (byte)cycles);
                _OpCodes[(int)_CurrentOp] = _Code;
                LogCpuInstructionTiming();
            }

            LogCpuInstruction(instructionSize, cycles);
            #endif
        }

        /// <summary>
        /// Logs the instruction.
        /// </summary>
        /// <param name="opcode">The opcode.</param>
        /// <param name="instructionSize">Size of the instruction.</param>
        /// <param name="cycles">The cycles.</param>
        [Conditional("CPU_INS_DEBUG")]
        private void LogCpuInstruction(int instructionSize, int cycles)
        {
            #if CPU_DEBUG
            string _InstructionName = _CurrentOp.ToString();
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)CpuEventType.InstructionProcessed,
                "{8:0000000##} I:0x{0:X4}\t {1}{11}  \t0x{2:X4} X={3:X2} Y={4:X2} A={5:X2} SP={6:X2} S={7} T:{9} SZ:{10}",
                _InstructionStart,
                _InstructionName,
                (instructionSize == 1 ? 0 : (instructionSize == 2 ? _BufferTemp[1] : (_BufferTemp[2] << 8 | _BufferTemp[1]))),
                _RegisterX,
                _RegisterY,
                _RegisterAccumulator,
                _RegisterStackPointer,
                _RegisterStatus,
                _InstructionCount,
                cycles,
                instructionSize,
                (_InstructionName.Length < 6 ?
                    (_InstructionName.Length < 5 ?
                    "   " : " ") : string.Empty)
            );

            if (instructionSize == 0 || cycles == 0)
            {
                TraceLogger.BreakIfAttached();
            }
            #endif
        }

        /// <summary>
        /// Logs the cpu instruction timing.
        /// </summary>
        /// <param name="size">The size.</param>
        /// <param name="cycles">The cycles.</param>
        [Conditional("CPU_TIMING_DEBUG")]
        private void LogCpuInstructionTiming()
        {
            #if CPU_DEBUG
            OpCode _Code = _OpCodes[(int)_CurrentOp];
            TraceLogger.TraceEvent(TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                0,
                _Code.ToString()
            );
            #endif
        }

        public void LogRead(byte data)
        {
            LogCpuMemory(true, (ushort)data);
        }


        public void LogRead(ushort data)
        {
            LogCpuMemory(true, data);
        }

        private void LogWrite(byte data)
        {
            LogCpuMemory(false, (ushort)data);

        }

        public void LogWrite(ushort data)
        {
            LogCpuMemory(false, data);
        }

        /// <summary>
        /// Logs the cpu memory read write.
        /// </summary>
        /// <param name="isRead">if set to <c>true</c> [is read].</param>
        /// <param name="address">The address.</param>
        /// <param name="data">The data.</param>
        [Conditional("CPU_MEM_DEBUG")]
        private void LogCpuMemory(bool isRead, ushort data)
        {
            #if CPU_DEBUG
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)(isRead ? CpuEventType.MemoryRead : CpuEventType.MemoryWrite),
                "{0:0000000##} I:0x{1:X4}\t {2}:0x{3:X4}\t0x{4:X4}",
                _InstructionCount,
                _InstructionStart,
                isRead ? "R" : "W",
                _MemoryAddress,
                data
            );
            #endif

        }

        /// <summary>
        /// Logs the interrupt.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="supressed">if set to <c>true</c> [supressed].</param>
        [Conditional("CPU_INT_DEBUG")]
        private void LogInterrupt(InterruptType type, bool supressed)
        {
            #if CPU_DEBUG
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)(CpuEventType.Interrupt),
                "{2:0000000##} Interrupt : {0} {1}",
                type.ToString(),
                supressed ? "SUPPRESSED" : string.Empty,
                _InstructionCount
            );
            #endif
        }


        /// <summary>
        /// Logs an item being pushed on to stack.
        /// </summary>
        /// <param name="address">The address.</param>
        [Conditional("CPU_STACK_DEBUG")]
        private void LogPush(ushort address)
        {
            #if CPU_DEBUG
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)(CpuEventType.StackPush),
                "{0:0000000##} I:0x{1:X4}\t S:0x{2:X4}\t \t A:0x{5:X4} {3:X2} TO {4:X2}\t\tPOP",
                _InstructionCount,
                _InstructionStart,
                CurrentStackAddress,
                _RegisterStackPointer,
                _RegisterStackPointer + 1,
                address
            );
            #endif
        }

        [Conditional("CPU_STACK_DEBUG")]
        private void LogPop(byte value)
        {

            #if CPU_DEBUG
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)(CpuEventType.StackPop),
                "{0:0000000##} I:0x{1:X4}\t R:{2:X4}\t 0x{2:X4}\t {3:X2} TO {4:X2}\t\tPOP",
                _InstructionCount,
                _InstructionStart,
                ((byte)(_RegisterStackPointer-1)) + _StackOffset,
                value,
                _RegisterStackPointer,
                _RegisterStackPointer-1
            );
            #endif
        }


        /// <summary>
        /// Logs a address poped off the stack.
        /// Call after poping the address.
        /// </summary>
        /// <param name="address">The address.</param>
        [Conditional("CPU_STACK_DEBUG")]
        private void LogPop(ushort value)
        {
            #if CPU_DEBUG
            TraceLogger.TraceEvent(
                TraceSubsystem.Cpu,
                TraceEventType.Verbose,
                (int)(CpuEventType.StackPop),
                "{0:0000000##} I:0x{1:X4}\t R:{2:X4}\t 0x{2:X4}\t {3:X2} TO {4:X2}\t\tPOP",
                _InstructionCount,
                _InstructionStart,
                ((byte)(_RegisterStackPointer-2)) + _StackOffset,
                value,
                _RegisterStackPointer,
                _RegisterStackPointer-2
            );
            #endif

        }
        #endregion Debug Methods

        #region Properties
        /// <summary>
        /// Gets the stack.
        /// </summary>
        /// <value>The stack.</value>
        public byte[] Stack
        {
            get
            {
                byte[] _Temp = new byte[0xFF];
                _Memory.Read(_Temp, 0x101, 0xFF);
                Array.Reverse(_Temp);
                return _Temp;

            }
        }
        #endregion Properties

        
    }
}
